//
// Created by dukang wang on 2024/12/19.
//

#include "../include/AudioRecorder.h"
#include "../AndroidLog.h"

namespace hy {

//录制音频时的回调
void AudioRecorderCallback(SLAndroidSimpleBufferQueueItf bufferQueueItf, void *context) {

    AudioRecorder *recorderContext = static_cast<AudioRecorder *>(context);
    if (recorderContext == nullptr) {
        LOGE("recorderContext is null")
        return;
    }

    if (recorderContext->buffer == nullptr) {
        LOGE("recorderContext buffer is null")
        return;
    }

    fwrite(recorderContext->buffer, recorderContext->bufferSize, 1, recorderContext->pfile);
    LOGD("save a frame audio data,pid=%ld", syscall(SYS_gettid))

    SLresult result;
    SLuint32 state;
    result = (*(recorderContext->recorderRecord))->GetRecordState(
            recorderContext->recorderRecord,
            &state
    );

    if (result != SL_RESULT_SUCCESS) {
        LOGE("recorderRecord GetRecordState failed result=%d", result)
        return;
    }
    LOGD("state=%d", state)

    //录制中
    if (state == SL_RECORDSTATE_RECORDING) {
        result = (*bufferQueueItf)->Enqueue(
                bufferQueueItf,
                recorderContext->buffer,
                recorderContext->bufferSize
        );

        LOGD("bufferQueueItf Enqueue result=%d", result)
    }
}

//创建录音类实例
AudioRecorder *AudioRecorder::CreateInstance() {
    return new AudioRecorder();
}

AudioRecorder::AudioRecorder() {

}

AudioRecorder::~AudioRecorder() {

}

//初始化引擎
int AudioRecorder::Init() {
    if (recorderObject != nullptr) {
        LOGI("Audio recorder already has bean created.")
        return 1;
    }

    SLEngineOption pEngineOptions[] = {
            (SLuint32) SL_ENGINEOPTION_THREADSAFE,
            (SLuint32) SL_BOOLEAN_TRUE
    };

    //创建引擎对象，调用全局方法创建一个引擎对象（OpenSLES唯一入口）
    SLresult result;
    result = slCreateEngine(
            &engineObject,//对象地址，传入要赋值的对象
            1,//配置参数数量
            pEngineOptions,//配置参数，为枚举数组
            0,//支持的接口数量
            nullptr,//具体的要支持的接口，是枚举数组
            nullptr//具体的要支持的接口是开放的还是关闭的，也是一个数组，这三个参数长度是一致的
    );

    if (result != SL_RESULT_SUCCESS) {
        LOGE("slCreateEngine failed result=%d", result)
        return -1;
    }

    //实例化这个对象 Realizing the Engine in synchronous mode.
    result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
    if (result != SL_RESULT_SUCCESS) {
        LOGE("engineObject Realize failed result=%d", result)
        return -1;
    }

    result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
    if (result != SL_RESULT_SUCCESS) {
        LOGE("engineObject GetInterface failed result=%d", result)
        return -1;
    }

    //设置IO设备（麦克风）输入输出，我们需要设置采集设备的一些输入输出配置。
    SLDataLocator_IODevice ioDevice = {
            SL_DATALOCATOR_IODEVICE, //类型，这里只能是SL_DATALOCATOR_IODEVICE
            SL_IODEVICE_AUDIOINPUT, //device类型，选择了音频输入类型
            SL_DEFAULTDEVICEID_AUDIOINPUT, //deviceID 对应的是SL_DEFAULTDEVICEID_AUDIOINPUT
            NULL //device实例
    };

    //数据源
    SLDataSource recSource = {
            &ioDevice,//SLDataLocator_IODevice配置输入
            NULL//输入格式，采集的并不需要
    };

    //数据源简单缓冲队列定位器，输出buffer队列
    SLDataLocator_AndroidSimpleBufferQueue recBufferQueue = {
            SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,//类型，这里只能是SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE
            numBufferQueue //buffer的数量
    };


    //PCM数据源格式，设置输入数据的格式
    SLDataFormat_PCM pcm = {
            SL_DATAFORMAT_PCM,//输入PCM格式的数据
            channels,//输出的声道数量
            SL_SAMPLINGRATE_44_1,//输出的采样频率
            SL_PCMSAMPLEFORMAT_FIXED_16,//输出的采样格式，这里是16bit
            SL_PCMSAMPLEFORMAT_FIXED_16,//一般来说，跟随上一个参数
            SL_SPEAKER_FRONT_LEFT |
            SL_SPEAKER_FRONT_RIGHT,//双声道配置,如果单声道可以使用SL_SPEAKER_FRONT_CENTER
            SL_BYTEORDER_LITTLEENDIAN//PCM数据的大小端排列
    };

    //输出
    SLDataSink dataSink = {
            &recBufferQueue,
            &pcm
    };

    //创建录制器，主要是创建录制对象和获取录制相关的接口

    //创建录制对象，并且指定开放SL_IID_ANDROIDSIMPLEBUFFERQUEUE这个接口
    SLInterfaceID iids[NUM_RECORDER_EXPLICIT_INTERFACES] = {
            SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
            SL_IID_ANDROIDCONFIGURATION
    };

    SLboolean required[NUM_RECORDER_EXPLICIT_INTERFACES] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};

    //创建audio recorder对象
    result = (*engineEngine)->CreateAudioRecorder(
            engineEngine,//引擎接口
            &recorderObject,//录制对象地址，用于传出对象
            &recSource,//输入配置
            &dataSink,//输出配置
            NUM_RECORDER_EXPLICIT_INTERFACES,//支持的接口数量
            iids,//具体的要支持的接口
            required);//具体的要支持的接口是开放的还是关闭的

    if (result != SL_RESULT_SUCCESS) {
        LOGE("engineEngine CreateAudioRecorder failed result=%d", result)
        return -1;
    }

    //get the android configuration interface
    result = (*recorderObject)->GetInterface(
            recorderObject,
            SL_IID_ANDROIDCONFIGURATION,
            &configItf);
    if (result != SL_RESULT_SUCCESS) {
        LOGE("recorderObject GetInterface configItf failed result=%d", result)
        return -1;
    }

    //实例化录制对象
    result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);
    if (result != SL_RESULT_SUCCESS) {
        LOGE("recorderObject Realize failed result=%d", result)
        return -1;
    }

    //获取Buffer接口
    result = (*recorderObject)->GetInterface(
            recorderObject,
            SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
            (void *) &recorderBuffQueueItf);
    if (result != SL_RESULT_SUCCESS) {
        LOGE("recorderObject GetInterface recorderBuffQueueItf failed result=%d", result)
        return -1;
    }

    //获取录制接口
    result = (*recorderObject)->GetInterface(
            recorderObject,
            SL_IID_RECORD,
            &recorderRecord
    );
    if (result != SL_RESULT_SUCCESS) {
        LOGE("recorderObject GetInterface recorderRecord failed result=%d", result)
        return -1;
    }

    result = (*recorderBuffQueueItf)->RegisterCallback(recorderBuffQueueItf,
                                                       AudioRecorderCallback, this);
    if (result != SL_RESULT_SUCCESS) {
        LOGE("recorderBuffQueueItf RegisterCallback result=%d", result)
        return -1;
    }


    return 0;
}


void AudioRecorder::startRecord(const std::string recordPath) {
    if (recorderRecord == nullptr) {
        LOGE("startRecord failed recorderRecord is null.")
        return;
    }
    AudioSrcPath = recordPath;

    pfile = fopen(AudioSrcPath.c_str(), "w");
    if (pfile == nullptr) {
        LOGE("fail to open file %s", AudioSrcPath.data())
        return;
    }

    SLresult result;

    //开始录制音频，设置录制状态
    result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
    if (result != SL_RESULT_SUCCESS) {
        LOGE("recorderRecord SetRecordState start failed result=%d", result)
        return;
    }

    buffer = new uint8_t[mBufferSize];
    bufferSize = mBufferSize;

    //在设置完录制状态后一定需要先Enqueue一次，才会开始采集回调
    result = (*recorderBuffQueueItf)->Enqueue(recorderBuffQueueItf, buffer, mBufferSize);
    if (result != SL_RESULT_SUCCESS) {
        LOGE("recorderBuffQueueItf Enqueue failed result=%d", result)
        return;
    }

    LOGI("Starting recording tid=%ld", syscall(SYS_gettid))
}

void AudioRecorder::stopRecord() {
    if (recorderRecord == nullptr) {
        LOGE("stopRecord recorderRecord is null")
        return;
    }

    SLresult result;
    result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
    if (result != SL_RESULT_SUCCESS) {
        LOGE("recorderRecord SetRecordState stop failed result=%d", result)
        return;
    }

    fclose(pfile);
    delete buffer;
    buffer = nullptr;
    LOGE("stopRecord done")
}

void AudioRecorder::release() {
    //只需要销毁OpenSLES对象，接口不需要做Destroy处理
    if (recorderObject != nullptr) {
        (*recorderObject)->Destroy(recorderObject);
        recorderObject = nullptr;
        recorderRecord = nullptr;
        recorderBuffQueueItf = nullptr;
        configItf = nullptr;
    }

    if (engineObject != nullptr) {
        (*engineObject)->Destroy(engineObject);
        engineObject = nullptr;
        engineEngine = nullptr;
    }
    LOGD("release done")
}

} // hy